include("cl_circle.lua")
include("cl_ghost.lua")
include("buildings.lua")
include("helditem.lua")
include("itemparser.lua")
include("recipes.lua")
include("shared.lua")
include("cl_inventory.lua")
include("cl_lootventory.lua")
include("cl_help.lua")
include("cl_targetid.lua")

Sound("itemopen.wav")
Sound("itemequip.wav")

surface.CreateFont("Trebuchet",24,400,true,false,"ScoreboardText")
surface.CreateFont("Trebuchet",30,400,true,false,"ScoreboardSub")
surface.CreateFont("Trebuchet",36,400,true,false,"ScoreboardHead")

GM.needs = {
	cold = 0,
	thirst = 0,
	hunger = 0,
	fatigue = 0,
	constitution = 100,
}

GM.net = {
	false,
}

function GM:Initialized()
	self.inv = {}
	self.invdat = {}
end

GM.LastThink = CurTime()
function GM:Think()
	local delta = CurTime() - self.LastThink
	self.LastThink = CurTime()
	
	if self:IsRoundStarted() and not LocalPlayer():WSIsPigeon() then
		self:DoDecay(delta)
	end
	self:Ghost()
end

GM.pulses = {
	cold = 0,
	thirst = 0,
	hunger = 0,
	fatigue = 0,
	constitution = 0,
}


GM.invsound = function()
	local GM = GM or gmod.GetGamemode()
	if UnPredictedCurTime() > GM.invsel.last + GM.invsel.fade + GM.invsel.fadetime then
		surface.PlaySound("itemopen.wav")
		GM.invsel.lastsound = UnPredictedCurTime()
	end
end
GM.invsoundclose = function(force) //This actually selects the items, it isn't just for sound
	local GM = GM or gmod.GetGamemode()
	if (
		(force and UnPredictedCurTime() < GM.invsel.last + GM.invsel.fade + .1) or
		(UnPredictedCurTime() > GM.invsel.last + GM.invsel.fade and UnPredictedCurTime() > GM.invsel.lastsound + GM.invsel.fade and UnPredictedCurTime() < GM.invsel.last + GM.invsel.fade + .1)
	) then
		return GM.slotinvquickclose()
	end
end
GM.slotinvquickclose = function()
	local self = gmod.GetGamemode()
	
	surface.PlaySound("itemequip.wav")
	
	local sel
	if self.invsel.items[self.invsel.select] then
		sel = self.invsel.items[self.invsel.select].class
	end
	
	//self.invsel.curclass = sel //FIXME: This should be UMSGed back
	
	if not sel then
		sel = "!none"
	end
	
	RunConsoleCommand("wssel",sel)
	
	self.invsel.lastsound = UnPredictedCurTime()
	
	self.invsel.last = UnPredictedCurTime()-self.invsel.fade
	
	//TODO: Make sure setting this to nil is useful
	if self.invdrag then
		self.invdrag.hoverslot = nil
	end
	return true
end

function GM:Has( class, qty )
	local self = gmod.GetGamemode()
	local qty = qty or 1
	if self.inv[class] then
		local dat = self.inv[class]
		if dat.quantity >= qty then
			return true
		end
	end
	return false 
end

GM.HUDPaintDelta = CurTime()
function GM.HUDPaint()
	local self = gmod.GetGamemode()
	self:HUDDrawTargetID()
	local xpos = ScrW()*.5
	local rot = UnPredictedCurTime()*.1%1*360
	local delta = CurTime() - self.HUDPaintDelta
	self.HUDPaintDelta = CurTime()
	
	//Round timer
	if self.RoundTime and (not self:IsRoundStarted()) and LocalPlayer():WSIsPigeon() then
		local disp = string.ToMinutesSeconds(self.RoundTime-CurTime())
		disp = "Preround "..disp
		
		surface.SetFont("Trebuchet24")
		local w,h = surface.GetTextSize(disp)
		draw.RoundedBox(4,16,16,w+8,24,Color(0,0,0,100))
		
		draw.DrawText(tostring(disp),"Trebuchet24",16+4,16,Color(255,255,255,255),0)
	end
	
	//Health Circle
	if self:IsRoundStarted() and LocalPlayer():Alive() and not LocalPlayer():WSIsPigeon() then
		draw.NoTexture()
		surface.SetDrawColor(0,0,0,50)
		epcircle.DrawCircle(xpos,ScrH()-64,32,nil,rot+0,360,64)
		
		local frac
		local pulse
		local pulses = self.pulses
		
		//draw.DrawText(tostring(LocalPlayer():GetCurrentCommand():KeyDown(IN_SPEED)),"Trebuchet24",0,0,Color(255,255,255,255),0)
		//draw.DrawText(tostring(LocalPlayer():GetVelocity()),"Trebuchet24",0,0,Color(255,255,255,255),0)
		//local speed = LocalPlayer():GetVelocity():Length()
		//draw.DrawText(tostring(speed),"Trebuchet24",0,0,Color(255,255,255,255),0)
		//draw.DrawText(" \n"..tostring(self.net[wsNet.CROUCH])..tostring(self:crouching()),"Trebuchet24",0,0,Color(255,255,255,255),0)
		//draw.DrawText(tostring(debug).."\n"..tostring(debuga),"Trebuchet24",0,0,Color(255,255,255,255),0)
		
		//Cold
		frac = (self.needs.cold or 0)/100
		pulses.cold = pulses.cold+frac*5*delta
		pulse = math.abs(math.sin(pulses.cold))
		surface.SetDrawColor(255,0,0,100)
		epcircle.DrawCircle(xpos,ScrH()-64,24*frac,8,rot+0,90,16)
		surface.SetDrawColor(255,0,0,195*(1-pulse)*(frac))
		epcircle.DrawCircle(xpos,ScrH()-64,32+128*pulse,8,rot+0,90,16)
		
		//Thirst
		frac = (self.needs.thirst or 0)/100
		pulses.thirst = pulses.thirst+frac*5*delta
		pulse = math.abs(math.sin(pulses.thirst))
		surface.SetDrawColor(0,0,255,100)
		epcircle.DrawCircle(xpos,ScrH()-64,24*frac,8,rot+270,90,16)
		surface.SetDrawColor(0,0,255,195*(1-pulse)*(frac))
		epcircle.DrawCircle(xpos,ScrH()-64,32+128*pulse,8,rot+270,90,16)
		
		//Hunger
		frac = (self.needs.hunger or 0)/100
		pulses.hunger = pulses.hunger+frac*5*delta
		pulse = math.abs(math.sin(pulses.hunger))
		surface.SetDrawColor(0,255,0,100)
		epcircle.DrawCircle(xpos,ScrH()-64,24*frac,8,rot+180,90,16)
		surface.SetDrawColor(0,255,0,195*(1-pulse)*(frac))
		epcircle.DrawCircle(xpos,ScrH()-64,32+128*pulse,8,rot+180,90,16)
		
		//Fatigue
		frac = (self.needs.fatigue or 0)/100
		pulses.fatigue = pulses.fatigue+frac*5*delta
		pulse = math.abs(math.sin(pulses.fatigue))
		surface.SetDrawColor(255,255,0,100)
		epcircle.DrawCircle(xpos,ScrH()-64,24*frac,8,rot+90,90,16)
		surface.SetDrawColor(255,255,0,195*(1-pulse)*(frac))
		epcircle.DrawCircle(xpos,ScrH()-64,32+128*pulse,8,rot+90,90,16)
		
		//Constitution
		local c = (100-(self.needs.constitution or 0))/10
		pulses.constitution = pulses.constitution+c*delta
		local m = math.cos(pulses.constitution)%2
		if m > 1 then
			 m = 2 - m
		end
		
		c = (self.needs.constitution or 0)/100
		surface.SetDrawColor(255*c,255*c,255*c,255*m)
		//surface.SetDrawColor(255,255,255,255*m)
		epcircle.DrawCircle(xpos,ScrH()-64,32,8,rot+0,360,64)
		//surface.SetDrawColor(255*c,255*c,255*c,195*(1-m)*(1-c))
		surface.SetDrawColor(255,255,255,255*(1-m)*(1-c))
		epcircle.DrawCircle(xpos,ScrH()-64,32+128*m,8,rot+0,360,128)
	end
	
	//Inventory
	local dat = self.invsel
	local ta = math.Clamp(1 - (UnPredictedCurTime() - dat.last - dat.fade)/dat.fadetime,0,1) //Total alpha
	
	
	if self.invdrag then
		self.invdrag.hoverslot = nil
	end
	
	local overloot = self.lootventory.isMouseOver()
	
	for i = dat.inc and 0 or 1,dat.inc and 6 or 7 do
		local k = i-1
		
		local s = i + dat.from - 7
		if s < 0 then
			s = s + 10
		end
		
		local alpha = 1
		if i == (dat.inc and 0 or 1) then
			alpha = 1-math.abs(dat.tween)
			if dat.inc then
				alpha = 1 - alpha
			end
		end
		
		alpha = alpha * ta
		
		local x = 16
		local y = ScrH()-(64+16)*7+(64+16)*(k+dat.tween)
		
		local highlight = false
		if self.invdrag then
			local m = {}
			m.x,m.y = gui.MousePos()
			
			if (not overloot) and m.x >= x and m.x <= x+64 and m.y >= y and m.y <= y+64 then //TODO: Make sure it isn't over the inventory
				highlight = true
				self.invdrag.hoverslot = s
			end
		end
		
		draw.RoundedBox(4,x,y,64,64,Color(0,highlight and 255 or 0,(s == dat.select and not highlight) and 255 or 0,100*alpha))
		if dat.items[s] then
			dat.items[s]:onInvPaint(x,y,64,64,alpha)
		end
		draw.DrawText(tostring(s),"Trebuchet24",x+4,y,Color(0,0,0,127*alpha),0)
	end
	for i = dat.inc and 7 or 8,dat.inc and 10 or 11 do
		local k = i-7
		
		local s = i + dat.from - 7
		if s > 9 then
			s = s - 10
		end
		
		local alpha = 1
		if i == (dat.inc and 10 or 11) then
			alpha = math.abs(dat.tween)
			if dat.inc then
				alpha = 1 - alpha
			end
		end
		
		alpha = alpha * ta
		
		local x = 16+(64+16)*(k+dat.tween)
		local y = ScrH()-16-64
		
		local highlight = false
		if self.invdrag then
			local m = {}
			m.x,m.y = gui.MousePos()
			
			if (not overloot) and m.x >= x and m.x <= x+64 and m.y >= y and m.y <= y+64 then
				highlight = true
				self.invdrag.hoverslot = s
			end
		end
		
		
		draw.RoundedBox(4,x,y,64,64,Color(0,highlight and 255 or 0,(s == dat.select and not highlight) and 255 or 0,100*alpha))
		if dat.items[s] then
			dat.items[s]:onInvPaint(x,y,64,64,alpha)
		end
		draw.DrawText(tostring(s),"Trebuchet24",x+4,y,Color(0,0,0,127*alpha),0)
	end
	
	draw.DrawText("> equip." .. (dat.items[dat.select] and dat.items[dat.select].label or "Nothing") ,"Trebuchet18",16+64+4,ScrH()-16-64-16-4,Color(255,255,255,255*ta),0)
	
	if dat.tween == 0 and dat.select ~= dat.from then
		/*//if dat.select < dat.from then
		//if (dat.from+4 >= dat.select or dat.from-6 >= dat.select) or dat.from + 5 == dat.select then //IncDec
		if math.abs(dat.from - dat.select) < 5 or math.abs(dat.from+10 - dat.select) < 5 then //DecInc
			dat.tween = -1 //Decrease
			dat.from = dat.from - 1 //Decrease
		else
			dat.tween = 1
			dat.from = dat.from + 1
		end*/
		
		local inc = false
		//for i = 1,5 do
		for i = 1,3 do //Slower, but prettier [insert butterfly]
			local k = dat.from + i
			if k > 9 then
				k = k - 10
			end
			if k == dat.select then
				inc = true
			end
		end
		
		if inc then
			dat.tween = 1
			dat.from = dat.from + 1
			dat.inc = true
		else
			dat.tween = -1 //Decrease
			dat.from = dat.from - 1 //Decrease
			dat.inc = false
		end
		
		if dat.from < 0 then
			dat.from = dat.from + 10
		elseif dat.from > 9 then
			dat.from = dat.from - 10
		end
	end
	
	dat.tween = math.Approach(dat.tween,0,delta*5)
	
	self.invsoundclose()
end

GM.invsel = {
	select = 0,
	from = 0,
	tween = 0,
	last = 0,
	lastsound = 0,
	fade = 1,
	fadetime = .5,
	items = {},
	curclass = nil, //Currently selected item
}

hook.Add("PlayerBindPress","gamemode",function(pl,bind,pressed)
	local self = gmod.GetGamemode()
	
	if self:IsRoundStarted() and not pl:WSIsPigeon() then //TODO: Close inventory when round is over
		local a = bind:find("slot")
		if a then
			local b = tonumber(bind:sub(a+4,a+4))
			if b then
				self.invsel.select = math.Clamp(b,0,9)
				self.invsound()
				self.invsel.last = UnPredictedCurTime()
				//return true //Some scripts use prehistoric menus
			end
		end
		if bind:find("invnext") then
			local a,b,err
			b = true
			a = self.inv[self.invsel.curclass]
			if a then
				a = a.item.onScroll
				if a then
					b,err = pcall(a,self.inv[self.invsel.curclass].item,1) //ITEM:onInvUnSelect(pl)
					if not b then
						Error("\n",err)
					end
				end
			end
			
			if b and not err then
				self.invsel.select = self.invsel.select + 1
				self.invsound()
				self.invsel.last = UnPredictedCurTime()
			end
		elseif bind:find("invprev") then
			local a,b,err
			b = true
			a = self.inv[self.invsel.curclass]
			if a then
				a = a.item.onScroll
				if a then
					b,err = pcall(a,self.inv[self.invsel.curclass].item,-1) //ITEM:onInvUnSelect(pl)
					if not b then
						Error("\n",err)
					end
				end
			end
			
			if b and not err then
				self.invsel.select = self.invsel.select - 1
				self.invsound()
				self.invsel.last = UnPredictedCurTime()
			end
		elseif bind:find("lastinv") then
			self.invsel.select = 0
			self.invsound()
			self.invsel.last = UnPredictedCurTime()
		end
		if self.invsel.select > 9 then
			self.invsel.select = 0
		elseif self.invsel.select < 0 then
			self.invsel.select = 9
		end
		
		if bind:find("+menu") or bind:find("+menu_context") then
			self.inventory.openInventory()
		end
		
		if bind:find("+attack") and pressed then //This covers +attack2 as well
			if self.invsoundclose(true) then
				return true
			end
		end
	end
end)

hook.Add("HUDShouldDraw","gamemode",function(n)
	if ({
		CHudWeaponSelection = true,
		CHudHealth = true,
		CHudBattery = true,
		CHudSecondaryAmmo = true,
		CHudAmmo = true,
		CHudWeapon = true,
	})[n] then
		return false
	end
end)

--'
//Networking
--'

usermessage.Hook("wsInv",function(msg)
	local self = gmod.GetGamemode()
	local relative = msg:ReadBool()
	if not relative then
		self.inv = {}
	end
	
	for i=1, msg:ReadShort() do
		local class = msg:ReadString()
		self.inv[class] = {
			item = self.items[class],
			quantity = msg:ReadShort(),
		}
		
		if self.inv[class].quantity <= 0 then
			for k,v in pairs(self.invsel.items) do
				if v.class == class then
					if self.invsel.select == k then
						self.invsel.curclass = nil //Good point to call on unselect //TODO: Check this
					end
					self.invsel.items[k] = nil
					//Msg("Item ",class," removed from slot ",k,"\n")
				end
			end
			self.inv[class] = nil
			//Msg("Item ",class," removed from inventory\n")
		end
	end
	
	if not relative then
		for k,v in pairs(self.invsel.items) do
			if not self.inv[k] then
				self.invsel.items[k] = nil
			end
		end
	end
end)

usermessage.Hook("wsNeeds",function(msg)
	local self = gmod.GetGamemode()
	self.needs.cold = msg:ReadFloat()
	self.needs.thirst = msg:ReadFloat()
	self.needs.hunger = msg:ReadFloat()
	self.needs.fatigue = msg:ReadFloat()
	self.needs.constitution = msg:ReadFloat()
end)

usermessage.Hook("wsTimer",function(msg)
	local self = gmod.GetGamemode()
	if msg:ReadBool() then
		self.RoundTime = msg:ReadFloat()
	else
		self.RoundTime = nil
	end
end)

local iBcache = {}
usermessage.Hook("iBdata",function(msg)
	local self = gmod.GetGamemode()
	
	local class = msg:ReadString()
	local num = msg:ReadShort()
	local tot = msg:ReadShort()
	local out = iBcache[class] or ""
	out = out..tostring(msg:ReadString())
	
	//Msg("Recv >",class,"< (",k," of ",tot,")\n")
	//Msg("\t",out,"\n")
	
	iBcache[class] = out
	
	if msg:ReadBool() then
		iBcache[class] = nil
		self:ParseBuilding(class,out)
	end
end)

usermessage.Hook("iBdataFin",function(msg)
	//gmod.GetGamemode():ParseBuildings()
end)

usermessage.Hook("wsSel",function(msg)
	local self = gmod.GetGamemode()
	local inv = self.inv
	local sel = msg:ReadString()
	local pl = LocalPlayer()
	
	local next = self.invsel.curclass
	
	if sel == "!none" or sel == "" then
		next = nil
	elseif self.items[sel] and inv[sel] then
		next = sel
	end
	
	if next ~= self.invsel.curclass then
		local prev = self.invsel.curclass
		
		//Msg("Changed from ",prev," to ",next,"\n")
		
		if prev then
			local a
			a = inv[prev]
			if a then
				a = a.item.onInvUnSelect
				if a then
					local b,err = pcall(a,inv[prev].item,pl) //ITEM:onInvUnSelect(pl)
					if not b then
						Error("\n",err)
					end
				end
			end
		end
		
		a = inv[next]
		if a then
			a = a.item.onInvSelect
			if a then
				local b,err = pcall(a,inv[next].item,pl) //ITEM:onInvSelect(pl)
				if not b then
					Error("\n",err)
				end
			end
		end
	end
	
	self.invsel.curclass = next
end)

usermessage.Hook("wsNet",function(msg)
	local self = gmod.GetGamemode()
	
	for k,v in pairs(self.net) do
		self.net[k] = msg:ReadBool()
	end
end)

FindMetaTable("Player").WSIsPigeon = function(self)
	if self ~= LocalPlayer() then
		error("WSIsPigeon called on non-local player.\n")
	end
	return gmod.GetGamemode().IsPigeon and true or false
end
usermessage.Hook("wsPigeonStatus",function(msg)
	local self = gmod.GetGamemode()
	self.IsPigeon = msg:ReadBool()
end)

usermessage.Hook("wsopt",function(um)
	local self = gmod.GetGamemode()
	local opt = um:ReadShort()
	if opt == 1 then
		self:ShowHelp()
	elseif opt == 2 then
		self:ShowTeam()
	elseif opt == 3 then
		//self:ShowSpare1()
		if not LocalPlayer():WSIsPigeon() then
			self.inventory.openInventory()
		end
	elseif opt == 4 then
		//self:ShowSpare2()
	end
end)

hook.Add("HUDPaint","wsWinner",function()
	local self = gmod.GetGamemode()
	if not self.winner then
		return
	end
	
	local d = self.winner
	if CurTime() < d.time then
		local disp = tostring(d.name)
		disp = disp.." wins!"
		
		surface.SetFont("Trebuchet24")
		local w,h = surface.GetTextSize(disp)
		draw.RoundedBox(4,(ScrW()-w)*.5-4,ScrH()*.5-12,w+8,24,Color(0,0,0,100))
		
		draw.DrawText(tostring(disp),"Trebuchet24",ScrW()*.5,ScrH()*.5-12,Color(255,255,255,255),1)
	end
end)
usermessage.Hook("wsWinner",function(msg)
	local self = gmod.GetGamemode()
	self.winner = {
		name = msg:ReadString(),
		time = msg:ReadFloat()+10,
	}
end)






//Macros

GM.crouching = function(self)
	if LocalPlayer():Alive() and self.net[wsNet.CROUCH] then
		local g = LocalPlayer():GetGroundEntity()
		if g:IsWorld() or g:IsValid() then
			return true
		end
	end
	return false
end

function GM:IsRoundStarted()
	if self.RoundTime and self.RoundTime <= CurTime() then
		return true
	else
		return false
	end
end










